﻿using System;
using Irony.Parsing;

namespace LibraryGuard.Internals
{
	/// <summary>
	/// Format grammar. See <see cref="NameFormatter"/>.
	/// </summary>
	public class FormatGrammar
		: Grammar
	{
		public FormatGrammar()
			: base(false)
		{
			#region Terminals
			var Identifier = new IdentifierTerminal("Identifier");
			Identifier.AstConfig.NodeCreator = AST.Identifier.Create;
			var Number = new NumberLiteral("Number", NumberOptions.Default, AST.Number.Create)
			{
				DefaultIntTypes = new TypeCode[] { TypeCode.Int32 }
			};
			var String = new StringLiteral("String", "\"", StringOptions.NoEscapes, AST.String.Create);
			var PureText = new PureTextTerminal("PureText");
			PureText.AstConfig.NodeCreator = AST.String.Create;
			var Boolean = new ConstantTerminal("Boolean", null);
			Boolean.AstConfig.NodeCreator = AST.Boolean.Create;
			Boolean.Add("true", true);
			Boolean.Add("false", false);
			Boolean.Add("1", true);
			Boolean.Add("0", false);
			#endregion

			#region NonTerminals
			var Value = new NonTerminal("Value"); //Transient
			var Parameter = new NonTerminal("Parameter"); //Transient
			var ParameterList = new NonTerminal("ParameterList", AST.ParameterList.Create);
			var FunctionCall = new NonTerminal("FunctionCall", AST.FunctionCall.Create);
			var Code = new NonTerminal("Code", AST.Code.Create);
			var CodeExpression = new NonTerminal("CodeExpression"); //Transient

			var ComparisonOperator = new NonTerminal("ComparisonOperator", (s, e) => { });
			var Condition = new NonTerminal("Condition", AST.Condition.Create);
			var ConditionExpression = new NonTerminal("ConditionExpression", AST.ConditionExpression.Create);

			var Entry = new NonTerminal("Entry"); //Transient
			var Format = new NonTerminal("Format", AST.Code.Create);
			#endregion

			#region Rules
			Value.Rule = String | Number | Boolean | Identifier | FunctionCall;

			Parameter.Rule = Value | Empty;
			ParameterList.Rule = MakeListRule(ParameterList, ToTerm(","), Parameter);
			FunctionCall.Rule = Identifier + "(" + ParameterList + ")";
			Code.Rule = MakeListRule(Code, ToTerm("+"), Value);
			CodeExpression.Rule = ToTerm("${") + Code + "}";

			ComparisonOperator.Rule = ToTerm("==") | "!=" | "<" | "<=" | ">" | ">=";

			Condition.Rule = Value + ComparisonOperator + Value;
			ConditionExpression.Rule = ToTerm("$[") + "(" + Condition + ")" + Code + "]";

			Entry.Rule = PureText | ConditionExpression | CodeExpression;
			Format.Rule = MakeListRule(Format, Empty, Entry);
			#endregion

			#region Settings
			this.MarkReservedWords("${", "$[");

			this.RegisterOperators(00, "==", "!=", "<", "<=", ">", ">=");

			this.MarkPunctuation("${", "}", "(", ")", "$[", "]");
			this.RegisterBracePair("${", "}");
			this.RegisterBracePair("(", ")");
			this.RegisterBracePair("$[", "]");
			this.MarkTransient(Entry, Value, Parameter, CodeExpression);

			this.LanguageFlags = LanguageFlags.CreateAst;
			this.Root = Format;
			#endregion
		}
	}
}
